/*
#Copyright (c) 2013, Philippe Bordron <philippe.bordron@gmail.com>
#
# This file is part of SIPPER.
#
# DCC is free software: you can redistribute it and/or modify
# it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# DCC is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU LESSER GENERAL PUBLIC LICENSE for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# along with SIPPER.  If not, see <http://www.gnu.org/licenses/>
*/
package combi.wip.sipper.filters;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.jgrapht.DirectedGraph;
import org.jgrapht.alg.ConnectivityInspector;
import org.jgrapht.graph.DefaultDirectedGraph;
import org.jgrapht.graph.DefaultEdge;


import combi.utils.Couple;


public class ExtractInfosResultat {

	private static boolean calculIntervalleMin = true;
	//private static ModScore modScore= new LogMoins1surX(1);
	//private static String pathPrefix = "./ecocyc - ALL-CHORISMATE-PWY - 53/";
	//private static String pathPrefix = "./eco/";
	//private static String pathPrefix = "./eco00010/";
	//private static String pathPrefix = "./metabolisme_central/";
	//private static String pathPrefix = "./eco00910_shuffle/";
	//private static String pathPrefix = "./eco00010_shuffle_fenetre/";
	//private static String pathPrefix = "./eco00010_fenetre/";
	//private static String pathPrefix = "./metabolisme_central_shuffle/";
	//private static String pathPrefix = "./metabolisme_central_fenetre/";
	//private static String pathPrefix = "./metabolisme_central_shuffle_fenetre/";
	//private static String pathPrefix = "./eco00010_shuffle_meta/";
	//private static String pathPrefix = "./metabolisme_central_shuffle_meta/";
	//private static String pathPrefix = "./eco00010_shuffle_meta_fenetre/";
	//private static String pathPrefix = "./metabolisme_central_shuffle_meta_fenetre/";
	//private static String fullPathPrefix = pathPrefix;
	//private static String fullPathPrefix = "./eco/";
	//private static String fullPathPrefix = "./eco_shuffle/";
	//private static String fullPathPrefix = "./eco_shuffle_fenetre/";
	//private static String pathPrefix = System.getProperty("user.home") + "/Data/CoExp/eco_U00096_GDS2588/";
	//private static String pathPrefix = System.getProperty("user.home") + "/Data/CoExp/eco_U00096_GDS2588_distanceAlternative/";
	//private static String pathPrefix = System.getProperty("user.home") + "/Data/Chili/Metacyc/ecoli/Gint_15.0/";
	//private static String pathPrefix = System.getProperty("user.home") + "/Data/Chili/Metacyc/afer243159/Gint/";
	private static String pathPrefix = System.getProperty("user.home") + "/Data/DistanceGenome/eco/";
	


	private static String ressources = System.getProperty("user.home") + "/Data/";
	
	//private static String ressources = System.getProperty("user.home") + "/Data/Chili/export/";
	//private static String ressources = System.getProperty("user.home") + "/Data/Chili/Metacyc/ecoli/15.0/data/";
	//private static String ressources =  System.getProperty("user.home") + "/Data/Chili/Metacyc/afer243159/14.1/data/";
	//private static String pathPrefix = "./data/";
	private static int k =1
	;
	public static void main(String[] args) {
		//String fileIn = pathPrefix + "cheminsTraduits.txt";
		//String fileIn = pathPrefix + "cheminsCouvrants_feuille_"+k+"ksp.txt";

		String fileIn = pathPrefix + "cheminsTraduits_10ksp.txt";
		//String fileIn = pathPrefix + "cheminsTraduits_"+k+"ksp.txt";
		//String fileIn = pathPrefix + "cheminsReduitsTraduits.txt";
		//String fileIn = pathPrefix + "cheminDecontract.txt";
		String fileOut = pathPrefix + "infoResultat"+ k +"ksp.txt";
		//String fileOut = pathPrefix + "infoResultatCouvrants"+ k +"ksp.txt";
		//String fileOut = pathPrefix + "infoResultatReduits"+ k +"ksp.txt";
		String fileOutReactions = pathPrefix + "infoResultatReactions"+ k +"ksp.txt";
		//String fileOutReactions = pathPrefix + "infoResultatReduitsReactions"+ k +"ksp.txt";
		//String fileOutReactions = pathPrefix + "infoResultatCouvrantsReactions"+ k +"ksp.txt";
		String fileOutGenes = pathPrefix + "infoResultatGenes"+ k +"ksp.txt";
		//String fileOutGenes = pathPrefix + "infoResultatReduitsGenes"+ k +"ksp.txt";
		//String fileOutGenes = pathPrefix + "infoResultatCouvrantsGenes"+ k +"ksp.txt";
		String fileBase = ressources + "genesPos.txt";
		//String fileBase = ressources + "genes.dat";
		//String fileBase = pathPrefix + "GDS2588_testM_moinslog.txt";
		//String fileBase = pathPrefix + "GDS2588_testM.txt";
		//String fileBase = fullPathPrefix + "GenePosShuffle.txt";
		String fileEquiv = pathPrefix + "redondances"+ k +".txt";

		//String dirIn = "/Users/bordronphilippe/Documents/Th�se/GOclusters/pvalue10";
		String dirIn = pathPrefix+"/pvalue"+ k ;


		Map<Integer, String> genome = new HashMap<Integer, String>();
		Map<String, Integer> genomeInv = new HashMap<String,Integer>();
		//Map<String, Integer> genomeDist = new HashMap<String,Integer>();
		//Map<Integer, Integer> equivGene = new HashMap<Integer, Integer>();
		loadGenome(fileBase, genome, genomeInv);

		Map<combi.utils.Couple<Double, Integer>, Integer> couples = new HashMap<Couple<Double, Integer>, Integer>();
		Map<Couple<String,String>, String> equivalence = new HashMap<Couple<String,String>, String>();
		Set<Couple<Couple<String, String>,Couple<String, String>>> edges = new HashSet<Couple<Couple<String, String>,Couple<String, String>>>();

		try{
			int nbReactions = 0;
			int nb = 0;
			int maxComptage = 0;
			Matcher m;
			Matcher m2;
			//String dd = "(?:[0-9]+(?:\\.[0-9]+)?)";
			String recoReaction = "(?:" +"[^ \t,\\]\\[)(]+" + ")";
			String beginBloc = "(?:(" + recoReaction + ")->(" + recoReaction + "))";
			String equiv = "(?:\\[.+\\]\t(.+-" + recoReaction + "-" + recoReaction + ")\t(.+))";
			String equivBis = "(?:-(" + recoReaction + ")-(" + recoReaction + "))";
			String flottant = "(?:[0-9]+(?:\\.[0-9]+)?)";
			String scores = "(?:(" + flottant + ")\t(" + flottant + ")\t(" + flottant + ")\t" +"(.+)" + ")"; //gloutonnerie
			//String geneBloc = "(?:(" + flottant + ")\t(.+)\t(b[0-9]{4})"  + ")";
			String geneBloc = "(?:(" + "[^ \t,\\]\\[)(]+" + "))";
			String reaction = "(?:(" + "[^ \t,\\]\\[)(]+" + "))";
			//String geneName = "(?:(" + flottant + ")\t(" + flottant + ")\t(" + flottant + ")\t" +"\\[\\{\\((?:\\+|-)b[0-9]{4}:([^,:]+),.+:([^,:]+),R[0-9]{5}\\),?\\},?\\]" + ")"; //gloutonnerie
			//String geneName = "(?:"+ "(?:" + ":([^,]+),(" + recoReaction  + ")\\))" + ")"; //gloutonnerie limit�e
			String geneName = "(?:"+ "(?:" + "\\([+-]?([^:]+)[^,]*,(" + recoReaction  + ")\\))" + ")"; //gloutonnerie limit�e

			String modName = "(?:^((?:M|eco)[0-9]{5})\t(.+)$)";
			String modIndice = "(?:^([0-9]+)\t(.+)$)";

			Pattern recoEquiv = Pattern.compile(equiv);
			Pattern recoEquivBis = Pattern.compile(equivBis);
			Pattern reco = Pattern.compile(beginBloc);
			Pattern recoScores = Pattern.compile(scores);
			//String recoGene = "(?:" + "([^:]+):(.+)" + ")";
			Pattern recoGene = Pattern.compile(geneBloc);
			Pattern recoGeneName = Pattern.compile(geneName);
			Pattern recoReact = Pattern.compile(","+reaction);

			//chargement des �quivalences de clusters de genes pour la pvalue.
			BufferedReader in;
			String ch;
			Couple<String, String> id = null;
			try
			{
				in = new BufferedReader (new FileReader (fileEquiv));

				while ((ch = in.readLine()) != null)
				{
					m = recoEquiv.matcher(ch);
					while (m.find())
					{
						m2 = recoEquivBis.matcher(m.group(2));
						while (m2.find())
						{
							equivalence.put(new Couple<String,String>(m2.group(1), m2.group(2)), m.group(1));
						}
					}
				}
				System.out.println(equivalence.size());
				in.close();
			}
			catch (Exception e)
			{
				System.err.println("Pas de fichier \""+ fileEquiv +"\"");
			}




			in = new BufferedReader (new FileReader (fileIn));
			BufferedWriter out;
			BufferedWriter outR;
			BufferedWriter outG;
			out = new BufferedWriter(new FileWriter(fileOut));
			outR = new BufferedWriter(new FileWriter(fileOutReactions));
			outG = new BufferedWriter(new FileWriter(fileOutGenes));
			out.write("id poids nb_genes nb_reactions taille_intervalle pvalue\n");

			Set<String> reactionsTrouvees = new HashSet<String>();
			Set<String> genes = new HashSet<String>();
			int limiteK = 0;
			int limiteMax = k;
			Double pvalue = null;
			Double poids = 0.0;
			boolean genesValides = false;
			Couple<String, String> oldCouple = null;
//			System.out.println(dist.get(genomeInv.get("b4119"), genomeInv.get("b4119")));
//			System.out.println(distance(genomeInv, dist,"b4119", "b4119"));
//			System.exit(1);
			while ((ch = in.readLine()) != null)
			{
				try
				{
					if (ch.isEmpty())
					{
						//System.err.println("ch.isEmpty()");
						double tailleIntervalle = 0;
						if (reactionsTrouvees.size() != 0 && genesValides && poids != null && genes.size() != 0)
						{
							DirectedGraph<Couple<String, String>, DefaultEdge> graph = new DefaultDirectedGraph<Couple<String, String>, DefaultEdge>(DefaultEdge.class);
							nb++;
							//out.write(distance(genomeInv, c.getFirst(), c.getSecond())+ " " + cpt + "\n");
							//Double poidsTot = (poids/(double)(limiteK-2));
							Double poidsTot = 0.0;
							for (Couple<Couple<String, String>, Couple<String, String>> c : edges)
							{
								//System.err.println(c.getFirst() + "->" + c.getSecond() + ":" + (double)distance(genomeInv,dist,  c.getFirst().getFirst(), c.getSecond().getFirst()));
								poidsTot = poidsTot + (double)distance(genomeInv,  c.getFirst().getFirst(), c.getSecond().getFirst());
								if (!graph.containsVertex(c.getFirst()))
								{
									graph.addVertex(c.getFirst());
								}
								if (!graph.containsVertex(c.getSecond()))
								{
									graph.addVertex(c.getSecond());
								}
								graph.addEdge(c.getFirst(), c.getSecond());

							}
							if (calculIntervalleMin)
								{
								tailleIntervalle = (double)intervalleMin(genomeInv, genes);
								}
							else
							{
								tailleIntervalle = 0.0;
							}
							ConnectivityInspector<Couple<String, String>, DefaultEdge> connexes = new ConnectivityInspector<Couple<String, String>, DefaultEdge>(graph);
							List<Set<Couple<String, String>>> composanteConnexeSet = connexes.connectedSets();
							if (composanteConnexeSet.size() >1)
							{
								System.out.println(composanteConnexeSet.size() + " sous-graphes");
							}
							for (Set<Couple<String, String>> set : composanteConnexeSet)
							{
								Set<String> intervalle = new HashSet<String>();
								for (Couple<String, String> c : set)
								{
									intervalle.add(c.getFirst());
								}
								//tailleIntervalle = tailleIntervalle + intervalleMin(genomeInv, intervalle);
							}
							if (poidsTot<0)
							{
								System.err.println(id +  " " + poidsTot);
								System.err.println(id +  " " + graph);
								for (DefaultEdge e :graph.edgeSet())
								{
									int taille = genome.size();
									int result = Math.abs(genomeInv.get(graph.getEdgeSource(e).getFirst()) - genomeInv.get(graph.getEdgeTarget(e).getFirst()));

									System.err.print((double)distance(genomeInv, graph.getEdgeSource(e).getFirst(), graph.getEdgeTarget(e).getFirst()) + "=min("+ genomeInv.get(graph.getEdgeSource(e).getFirst()) + "-" + genomeInv.get(graph.getEdgeTarget(e).getFirst()) + "=" +result +","  +  genomeInv.size() + "-" + result+ "="+(genomeInv.size() - result) +")" +" ");
								}
								System.err.println();

							}
							if (pvalue == null)
							{
								out.write(nb + " " + poidsTot + " " + genes.size() + " " + reactionsTrouvees.size() + " " + tailleIntervalle + " NA\n");
							}
							else{
								out.write(nb + " " + poidsTot + " " + genes.size() + " " + reactionsTrouvees.size() + " " + tailleIntervalle + " " + pvalue + "\n");
							}

							outR.write(nb + "\t");
							for(String r : reactionsTrouvees)
							{
								outR.write(r+" ");
							}
							outR.write("\n");

							outG.write(nb + "\t");
							for(String r : genes)
							{
								outG.write(r+" ");
							}
							outG.write("\n");
						}
						//raz
						id = null;
						limiteK = -1;
						reactionsTrouvees = new HashSet<String>();
						genes = new HashSet<String>();
						pvalue = null;
						poids = 0.0;
						oldCouple = null;
						edges = new HashSet<Couple<Couple<String, String>,Couple<String, String>>>();
					}
					if (limiteK <= limiteMax)
					{
						m = reco.matcher(ch);
						//reconnaissance des Rx -> Ry
						if (m.find())
						{
							id = new Couple<String, String>(new String(m.group(1)),new String(m.group(2)));
							//System.err.println(id);
							if (equivalence.containsKey(id))
							{
								pvalue = pvalueMin(dirIn+"/"+equivalence.get(id)+".terms");
							}
						}
						//System.err.println(id);

						m = recoGeneName.matcher(ch);
						oldCouple = null;
						while(m.find())
						{
							Couple<String, String> newCouple = new Couple<String, String> (new String(m.group(1)), new String (m.group(2)));
							//System.err.println(oldCouple + "->" + newCouple);
							if (oldCouple != null)
							{
								edges.add(new Couple<Couple<String, String>, Couple<String, String>>(oldCouple, newCouple));
							}
							genes.add(newCouple.getFirst());							
							genesValides = true;
							oldCouple = newCouple;
						}

						m = recoScores.matcher(ch);
						//String nbGenes = null;
						while (m.find())
						{
							poids = poids + Double.valueOf(m.group(1)); //faire la moyenne ou somme?
							//nbGenes = m.group(2);
						}

						m = recoReact.matcher(ch);
						//nbReactions = 0;
						while (m.find())
						{
							reactionsTrouvees.add(new String(m.group(1)));
							//nbReactions++;
						}
						limiteK++;
					}
				}
				catch (Exception e)
				{
					e.printStackTrace();
				}
				//enregistrement des valeurs extraitent dans structure.
			}
			double tailleIntervalle = 0;
			if (reactionsTrouvees.size() != 0 && genesValides && poids != null && genes.size() != 0)
			{
				DirectedGraph<Couple<String, String>, DefaultEdge> graph = new DefaultDirectedGraph<Couple<String, String>, DefaultEdge>(DefaultEdge.class);
				nb++;
				//out.write(distance(genomeInv, c.getFirst(), c.getSecond())+ " " + cpt + "\n");
				//Double poidsTot = (poids/(double)(limiteK-2));
				Double poidsTot = 0.0;
				for (Couple<Couple<String, String>, Couple<String, String>> c : edges)
				{
					poidsTot = poidsTot + (double)distance(genomeInv, c.getFirst().getFirst(), c.getSecond().getFirst());
					if (!graph.containsVertex(c.getFirst()))
					{
						graph.addVertex(c.getFirst());
					}
					if (!graph.containsVertex(c.getSecond()))
					{
						graph.addVertex(c.getSecond());
					}
					graph.addEdge(c.getFirst(), c.getSecond());

				}
				//tailleIntervalle = (double)intervalleMin(genomeInv, genes);
				tailleIntervalle = 0.0;
				ConnectivityInspector<Couple<String, String>, DefaultEdge> connexes = new ConnectivityInspector<Couple<String, String>, DefaultEdge>(graph);
				if (connexes.connectedSets().size() >1)
				{
					System.out.println(connexes.connectedSets().size());
				}
				for (Set<Couple<String, String>> set : connexes.connectedSets())
				{
					Set<String> intervalle = new HashSet<String>();
					for (Couple<String, String> c : set)
					{
						intervalle.add(c.getFirst());
					}
					tailleIntervalle = tailleIntervalle + intervalleMin(genomeInv, intervalle);
				}

				if (pvalue == null)
				{
					out.write(nb + " " + poidsTot + " " + genes.size() + " " + reactionsTrouvees.size() + " " + tailleIntervalle + " NA\n");
				}
				else{
					out.write(nb + " " + poidsTot + " " + genes.size() + " " + reactionsTrouvees.size() + " " + tailleIntervalle + " " + pvalue + "\n");
				}

				outR.write(nb + "\t");
				for(String r : reactionsTrouvees)
				{
					outR.write(r+" ");
				}
				outR.write("\n");

				outG.write(nb + "\t");
				for(String r : genes)
				{
					outG.write(r+" ");
				}
				outG.write("\n");
			}
			in.close();
			out.close();
			outR.close();
			outG.close();


		}catch(Exception e){
			e.printStackTrace();
			System.exit(1);
		}
		finally
		{
		}
	}


	public static SortedSet<String> createOrderedGenome(Map<String, Map<String, Collection<String>>> genes, Collection<String> genesException)
	{
		SortedSet<String> genome = new TreeSet<String>(new CompareGeneEx<String>(genes, genesException));
		genome.addAll(genes.keySet());
		return genome;
	}
	public static class CompareGeneEx<T> implements Comparator<T>
	{

		private Map<String, Map<String, Collection<String>>> map;
		private Collection<T> genesException;
		private String rightPos = "RIGHT-END-POSITION";
		private String leftPos = "LEFT-END-POSITION";


		public CompareGeneEx(Map<String, Map<String, Collection<String>>> map, Collection<T> genesException)
		{
			this.map = map;
			this.genesException = genesException;
		}

		@Override
		public int compare(T o1, T o2) {
			if (genesException.contains(o1) || genesException.contains(o2))
			{
				return 1;
			}
			int result = Integer.parseInt(map.get(o1).get(leftPos).iterator().next().toString()) - Integer.parseInt(map.get(o2).get(leftPos).iterator().next().toString());
			if (result == 0)
			{
				result = Integer.parseInt(map.get(o1).get(rightPos).iterator().next().toString()) - Integer.parseInt(map.get(o2).get(rightPos).iterator().next().toString());
			}
			return result;
		}

	}
	private static void loadGenome(String fileIn, Map<Integer, String> genome,
			Map<String, Integer> genomeInv) {
		Map<String, Map<String, Collection<String>>> genes = new HashMap<String, Map<String, Collection<String>>>();
		combi.sipper.io.InFlatFile genesIn = new combi.sipper.io.InFlatFile(fileIn);
		try {
			genes = genesIn.readFile();
			Collection<String> distanceGeneException = new HashSet<String>();
			String rightPos = "RIGHT-END-POSITION";
			String leftPos = "LEFT-END-POSITION";
			String geneProduct = "PRODUCT";

			Set<String> typesGenes = new HashSet<String>();
			for (Entry<String, Map<String, Collection<String>>> entry : genes.entrySet())
			{
				//gene sans position sur le genome
				if (!entry.getValue().containsKey(leftPos) || !entry.getValue().containsKey(rightPos))
				{
					distanceGeneException.add(entry.getKey());
				}
			}
			SortedSet<String> genePos = createOrderedGenome(genes, distanceGeneException);
			
			//String recoGene = "(?:" + "([^:]+):(.+)" + ")";
			int pos = 0;
			for(String s : genePos)
			{
				genome.put(pos, s);
				genomeInv.put(s, pos);
				pos++;
			}		
			System.out.println("Taille genome : " + genome.size() + "," + genomeInv.size());
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NumberFormatException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}



	private static double distance(Map<String, Integer> genome, String g1, String g2)
	{
		int taille = genome.size();
		int result = Math.abs(genome.get(g1) - genome.get(g2));
		if (taille < 2*result)
		{
			return taille - result;
		}
		return result;
	}


//	private static String bijString(List<Integer> set)
//	{
//		String result = "[";
//		for (Integer i : set)
//		{
//			/*result = result + ("(" + bijection.get(i).getFirst().getFirst()
//						+ "," + bijection.get(i).getSecond() + "),");*/
//			result = result + ("(" + bijection.get(i).getFirst().getFirst() + ":" + bijection.get(i).getFirst().getSecond()
//					+ "," + bijection.get(i).getSecond() + "),");
//		}
//		result = result + ("]");
//		//System.out.println(result);
//		return result;
//	}

	private static int intervalleMin(Map<String, Integer> genome, Collection<String> genes)
	{
		List<String> l = new ArrayList<String>(genes);
		Collections.sort(l, new CompareGene(genome));
		String pred = null;
		int max = 0;
		for (String g : l)
		{
			if (pred != null)
			{
				int score = genome.get(g) - genome.get(pred);
				if (score > max)
				{
					max = score;
				}
			}
			pred = g;
		}
		if (!l.isEmpty())
		{
			int score = genome.size() - (genome.get(l.get(l.size()-1)) - genome.get(l.get(0)));
			if (score > max)
			{
				max = score;
			}
		}
		max = genome.size() - max;
		return max + 1;
	}

	private static class CompareGene implements Comparator<String>
	{
		Map<String, Integer> genome;

		public CompareGene(Map<String, Integer> genome)
		{
			this.genome = genome;
		}

		@Override
		public int compare(String o1, String o2) {
			// TODO Auto-generated method stub
			return genome.get(o1).compareTo(genome.get(o2));
		}

	}



	private static Double pvalueMin(String fileIn)
	{
		Double result = Double.POSITIVE_INFINITY;
		//tenir compte de P, C et F ?
		try 
		{
			String ch;
			BufferedReader in = new BufferedReader (new FileReader (fileIn));
			Matcher m;
			String tag = "^CORRECTED P-VALUE\t(.+)$";
			Pattern reco = Pattern.compile(tag);
			while ((ch = in.readLine()) != null)
			{
				//reconnaissance
				m = reco.matcher(ch);
				while (m.find())
				{
					Double candidat = Double.valueOf(m.group(1));
					if (candidat<result)
					{
						result = candidat;
					}
				}
			}
		} 
		catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if (result.isInfinite())
			result = null;
		return result;
	}

	private static String printList (List<String> l)
	{
		String result = new String();
		for (String ch : l)
		{
			result = result + ch + " ";
		}
		return result;
	}


}
